package com.h2.ref.server.util.sec.signed;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.NullRememberMeServices;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.util.Assert;
import org.springframework.web.filter.GenericFilterBean;

public class SignedAuthenticationFilter extends GenericFilterBean {
   
   public static final String API_SIG = "api_sig";
   
   ////
   
   private AuthenticationManager _authenticationManager;
   
   private RememberMeServices _rememberMeServices;
   
   private boolean continueChainBeforeSuccessfulAuthentication = false;
   
   ////
   ////

   protected SignedAuthenticationFilter(String defaultFilterProcessesUrl) {
      
   }
   
   @Override
   public void afterPropertiesSet() {
      
      Assert.notNull(getAuthenticationManager(),
            "authenticationManager must be specified");
      
      if (getRememberMeServices() == null) {
         setRememberMeServices(new NullRememberMeServices());
     }
   }

   @Override
   public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
   throws IOException, ServletException {

      HttpServletRequest request = (HttpServletRequest) req;
      HttpServletResponse response = (HttpServletResponse) res;

      if (!requiresAuthentication(request, response)) {
          chain.doFilter(request, response);

          return;
      }
      
      Authentication authResult;

      try {
          authResult = attemptAuthentication(request, response);
          if (authResult == null) {
              // return immediately as subclass has indicated that it hasn't completed authentication
              return;
          }
      }
      catch (AuthenticationException failed) {
          // Authentication failed
          unsuccessfulAuthentication(request, response, failed);

          return;
      }

      // Authentication success
      if (continueChainBeforeSuccessfulAuthentication) {
          chain.doFilter(request, response);
      }

      successfulAuthentication(request, response, authResult);
      
   }

   private void successfulAuthentication(HttpServletRequest request,
         HttpServletResponse response, Authentication authResult) {

      SecurityContextHolder.getContext().setAuthentication(authResult);

      getRememberMeServices().loginSuccess(request, response, authResult);
      
   }

   private void unsuccessfulAuthentication(HttpServletRequest request,
         HttpServletResponse response, AuthenticationException failed) {
      SecurityContextHolder.clearContext();
      
      getRememberMeServices().loginFail(request, response);
      
   }

   private Authentication attemptAuthentication(HttpServletRequest request,
         HttpServletResponse response) {
      // TODO Auto-generated method stub
      return null;
   }

   protected boolean requiresAuthentication(HttpServletRequest request,
         HttpServletResponse response) {
      return false;
   }

   /**
    * @return the rememberMeServices
    */
   public RememberMeServices getRememberMeServices() {
      return _rememberMeServices;
   }

   /**
    * @param rememberMeServices the rememberMeServices to set
    */
   public void setRememberMeServices(RememberMeServices rememberMeServices) {
      _rememberMeServices = rememberMeServices;
   }

   /**
    * @return the authenticationManager
    */
   public AuthenticationManager getAuthenticationManager() {
      return _authenticationManager;
   }

   /**
    * @param authenticationManager the authenticationManager to set
    */
   public void setAuthenticationManager(AuthenticationManager authenticationManager) {
      _authenticationManager = authenticationManager;
   }

}
